<template>
  <div class="advanced-upload">
    <el-card class="upload-card">
      <template #header>
        <div class="card-header">
          <span>{{ title }}</span>
          <el-button v-if="!uploading && !uploadSuccess" type="primary" @click="handleSelectFile">选择文件</el-button>
          <input
            ref="fileInput"
            type="file"
            style="display: none"
            @change="handleFileChange"
            :accept="accept"
          />
        </div>
      </template>

      <div v-if="selectedFile" class="file-info">
        <div class="file-name">
          <el-icon>
            <Document />
          </el-icon>
          <span>{{ selectedFile.name }}</span>
          <span class="file-size">({{ formatFileSize(selectedFile.size) }})</span>
        </div>

        <div v-if="uploading" class="upload-progress">
          <el-progress
            :percentage="uploadProgress"
            :status="uploadProgress === 100 ? 'success' : ''"
            :stroke-width="15"
          />
          <div class="progress-info">
            <span>{{ formatFileSize(uploadedSize) }} / {{ formatFileSize(selectedFile.size) }}</span>
            <span>{{ uploadSpeed }}</span>
          </div>
          <el-button v-if="uploadProgress < 100" type="danger" @click="cancelUpload">取消上传</el-button>
        </div>

        <div v-if="!uploading && !uploadSuccess && fileReady" class="upload-actions">
          <el-button type="primary" @click="startUpload">开始上传</el-button>
          <el-button @click="resetUpload">重置</el-button>
        </div>

        <div v-if="!uploading && !uploadSuccess && !fileReady" class="preparing-file">
          <el-progress
            :indeterminate="true"
            :duration="3"
          />
          <div class="preparing-text">正在准备文件，请稍候...</div>
        </div>

        <div v-if="uploadSuccess" class="upload-success">
          <el-icon>
            <CircleCheckFilled />
          </el-icon>
          <span>上传成功</span>
          <el-button type="primary" link @click="copyFileUrl">复制链接</el-button>
          <el-button type="success" link @click="resetUpload">继续上传</el-button>
        </div>
      </div>

      <div
        v-else
        class="upload-placeholder"
        @dragover.prevent="onDragOver"
        @dragleave.prevent="onDragLeave"
        @drop.prevent="onDrop"
        :class="{ 'drag-over': isDragging }"
        @click="handleSelectFile"
      >
        <el-icon>
          <Upload />
        </el-icon>
        <span>拖拽文件到此处或点击选择文件</span>
        <span class="upload-tip">支持{{ accept || '所有' }}类型文件，最大{{ formatFileSize(maxFileSize) }}</span>
      </div>
    </el-card>
  </div>
</template>

<script setup>
import { ref, computed, onBeforeUnmount } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Document, Upload, CircleCheckFilled } from '@element-plus/icons-vue'
import SparkMD5 from 'spark-md5'

const props = defineProps({
  title: {
    type: String,
    default: '文件上传'
  },
  accept: {
    type: String,
    default: ''
  },
  maxFileSize: {
    type: Number,
    default: 1024 * 1024 * 1024 // 默认1GB
  },
  chunkSize: {
    type: Number,
    default: 2 * 1024 * 1024 // 默认2MB
  },
  baseUrl: {
    type: String,
    default: import.meta.env.VITE_BASE_API + '/common'
  }
})

const emit = defineEmits(['upload-success', 'upload-error', 'upload-progress'])

// 文件相关
const fileInput = ref(null)
const selectedFile = ref(null)
const fileMd5 = ref('')
const fileUrl = ref('')
const fileReady = ref(false) // 新增：标记文件是否准备好可以上传

// 上传状态
const uploading = ref(false)
const uploadSuccess = ref(false)
const uploadProgress = ref(0)
const uploadedSize = ref(0)
const uploadSpeed = ref('0 KB/s')
const uploadStartTime = ref(0)
const shouldCancelUpload = ref(false)

// 分片相关
const chunks = ref([])
const chunkCount = ref(0)
const uploadedChunks = ref(new Set())

// 计算上传速度的定时器
let speedTimer = null

// 选择文件
const handleSelectFile = () => {
  fileInput.value.click()
}

// 文件选择变更
// const handleFileChange = async (e) => {
//   const file = e.target.files[0];
//   if (!file) return;
//
//   // 检查文件大小
//   if (file.size > props.maxFileSize) {
//     ElMessage.error(`文件大小不能超过 ${formatFileSize(props.maxFileSize)}`);
//     return;
//   }
//
//   selectedFile.value = file;
//   uploadSuccess.value = false;
//   uploadProgress.value = 0;
//   uploadedSize.value = 0;
//   fileReady.value = false; // 重置文件准备状态
//
//   // 计算文件MD5
//   ElMessage.info('正在计算文件特征值，请稍候...');
//   try {
//     fileMd5.value = await calculateFileMd5(file);
//     // 检查文件是否已存在（秒传）
//     const checkResult = await checkFileExists(fileMd5.value);
//     if (checkResult.exists) {
//       fileUrl.value = checkResult.url;
//       handleUploadSuccess(fileUrl.value);
//       ElMessage.success('文件已存在，秒传成功！');
//     } else {
//       // 准备分片上传
//       prepareChunks();
//     }
//   } catch (error) {
//     console.error('计算MD5失败', error);
//     ElMessage.error('文件处理失败，请重试');
//   }
// };

// 计算文件MD5
const calculateFileMd5 = (file) => {
  return new Promise((resolve, reject) => {
    const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
    const chunkSize = 2 * 1024 * 1024 // 2MB
    const chunks = Math.ceil(file.size / chunkSize)
    let currentChunk = 0
    const spark = new SparkMD5.ArrayBuffer()
    const fileReader = new FileReader()

    fileReader.onload = (e) => {
      spark.append(e.target.result)
      currentChunk++

      if (currentChunk < chunks) {
        loadNext()
      } else {
        resolve(spark.end())
      }
    }

    fileReader.onerror = () => {
      reject('文件读取失败')
    }

    function loadNext() {
      const start = currentChunk * chunkSize
      const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
      fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
    }

    loadNext()
  })
}

// 检查文件是否已存在
const checkFileExists = async (md5) => {
  try {
    const response = await fetch(`${props.baseUrl}/checkFile?md5=${md5}`, {
      method: 'POST'
    })
    const result = await response.json()

    if (result.code === 1 && result.data) {
      return { exists: true, url: result.data }
    }
    return { exists: false }
  } catch (error) {
    console.error('检查文件失败', error)
    return { exists: false }
  }
}

// 准备分片
const prepareChunks = () => {
  fileReady.value = false // 开始准备前，设置为false
  const file = selectedFile.value
  const size = file.size
  const chunkSize = props.chunkSize

  // 计算分片数量
  chunkCount.value = Math.ceil(size / chunkSize)
  chunks.value = []

  // 创建分片
  for (let i = 0; i < chunkCount.value; i++) {
    const start = i * chunkSize
    const end = Math.min(size, start + chunkSize)
    chunks.value.push({
      index: i,
      file: file.slice(start, end)
    })
  }

  fileReady.value = true // 分片准备完成，设置为true
  ElMessage.success(`文件已准备好，共 ${chunkCount.value} 个分片`)
}

// 开始上传
const startUpload = async () => {
  if (!selectedFile.value || uploading.value) return

  uploading.value = true
  uploadStartTime.value = Date.now()
  shouldCancelUpload.value = false

  // 启动速度计算定时器
  speedTimer = setInterval(calculateSpeed, 1000)

  try {
    // 检查已上传的分片
    await checkUploadedChunks()

    // 上传分片
    await uploadChunks()

    // 如果上传被取消，不执行合并
    if (shouldCancelUpload.value) {
      resetUploadState()
      return
    }

    // 合并分片
    if (uploadedChunks.value.size === chunkCount.value) {
      await mergeChunks()
    }
  } catch (error) {
    console.error('上传失败', error)
    ElMessage.error('上传失败，请重试')
    resetUploadState()
  }
}

// 检查已上传的分片
const checkUploadedChunks = async () => {
  uploadedChunks.value = new Set()

  for (let i = 0; i < chunkCount.value; i++) {
    if (shouldCancelUpload.value) break

    try {
      const response = await fetch(`${props.baseUrl}/checkChunk?md5=${fileMd5.value}&chunkIndex=${i}`, {
        method: 'POST'
      })
      const result = await response.json()

      if (result.code === 1 && result.data === true) {
        uploadedChunks.value.add(i)
        uploadedSize.value += chunks.value[i].file.size
      }
    } catch (error) {
      console.error(`检查分片 ${i} 失败`, error)
    }
  }

  // 更新进度
  updateProgress()
}

// 上传分片
const uploadChunks = async () => {
  const uploadPromises = []

  for (let i = 0; i < chunkCount.value; i++) {
    if (uploadedChunks.value.has(i)) continue
    if (shouldCancelUpload.value) break

    uploadPromises.push(uploadChunk(chunks.value[i]))
  }

  await Promise.all(uploadPromises)
}

// 上传单个分片
const uploadChunk = async (chunk) => {
  const formData = new FormData()
  formData.append('file', chunk.file)
  formData.append('md5', fileMd5.value)
  formData.append('chunkIndex', chunk.index)
  formData.append('chunks', chunkCount.value)

  try {
    const response = await fetch(`${props.baseUrl}/uploadChunk`, {
      method: 'POST',
      body: formData
    })

    const result = await response.json()

    if (result.code === 1) {
      uploadedChunks.value.add(chunk.index)
      uploadedSize.value += chunk.file.size
      updateProgress()
    } else {
      throw new Error(result.message || '上传分片失败')
    }
  } catch (error) {
    console.error(`上传分片 ${chunk.index} 失败`, error)
    throw error
  }
}

// 合并分片
const mergeChunks = async () => {
  try {
    const formData = new FormData()
    formData.append('md5', fileMd5.value)
    formData.append('fileName', selectedFile.value.name)
    formData.append('chunks', chunkCount.value)

    const response = await fetch(`${props.baseUrl}/mergeChunks`, {
      method: 'POST',
      body: formData
    })

    const result = await response.json()

    if (result.code === 1) {
      fileUrl.value = result.data
      handleUploadSuccess(fileUrl.value)
    } else {
      throw new Error(result.message || '合并分片失败')
    }
  } catch (error) {
    console.error('合并分片失败', error)
    ElMessage.error('文件合并失败，请重试')
    resetUploadState()
  }
}

// 更新进度
const updateProgress = () => {
  if (!selectedFile.value) return

  const progress = Math.floor((uploadedSize.value / selectedFile.value.size) * 100)
  uploadProgress.value = progress

  emit('upload-progress', {
    progress,
    uploadedSize: uploadedSize.value,
    totalSize: selectedFile.value.size
  })
}

// 计算上传速度
const calculateSpeed = () => {
  if (!uploading.value || !uploadStartTime.value) return

  const now = Date.now()
  const timeElapsed = (now - uploadStartTime.value) / 1000 // 秒
  const bytesPerSecond = uploadedSize.value / timeElapsed

  uploadSpeed.value = `${formatFileSize(bytesPerSecond)}/s`
}

// 上传成功处理
const handleUploadSuccess = (url) => {
  uploadSuccess.value = true
  uploading.value = false
  uploadProgress.value = 100

  emit('upload-success', {
    url,
    fileName: selectedFile.value.name,
    fileSize: selectedFile.value.size,
    fileMd5: fileMd5.value
  })

  ElMessage.success('文件上传成功')
  clearInterval(speedTimer)
}

// 取消上传
const cancelUpload = () => {
  ElMessageBox.confirm('确定要取消上传吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    shouldCancelUpload.value = true
    resetUploadState()
    ElMessage.info('上传已取消')
  }).catch(() => {
  })
}

// 重置上传状态
const resetUploadState = () => {
  uploading.value = false
  clearInterval(speedTimer)
}

// 重置上传
const resetUpload = () => {
  selectedFile.value = null
  fileMd5.value = ''
  fileUrl.value = ''
  uploadSuccess.value = false
  uploadProgress.value = 0
  uploadedSize.value = 0
  uploadSpeed.value = '0 KB/s'
  uploadedChunks.value = new Set()
  chunks.value = []
  chunkCount.value = 0
  fileReady.value = false // 重置文件准备状态

  // 重置文件输入框
  if (fileInput.value) {
    fileInput.value.value = ''
  }
}

// 复制文件URL
const copyFileUrl = () => {
  if (!fileUrl.value) return

  navigator.clipboard.writeText(fileUrl.value)
    .then(() => {
      ElMessage.success('链接已复制到剪贴板')
    })
    .catch(() => {
      ElMessage.error('复制失败，请手动复制')
    })
}

// 格式化文件大小
const formatFileSize = (bytes) => {
  if (bytes === 0) return '0 B'

  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))

  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 组件销毁前清理
onBeforeUnmount(() => {
  clearInterval(speedTimer)
})
// 添加拖拽相关状态
const isDragging = ref(false)

// 拖拽相关方法
const onDragOver = () => {
  isDragging.value = true
}

const onDragLeave = () => {
  isDragging.value = false
}

const onDrop = (e) => {
  isDragging.value = false
  const files = e.dataTransfer.files
  if (files.length === 0) return

  // 只处理第一个文件
  const file = files[0]

  // 检查文件类型（如果有accept限制）
  if (props.accept && !isFileTypeAccepted(file, props.accept)) {
    ElMessage.error(`不支持的文件类型，请上传${props.accept}类型的文件`)
    return
  }

  // 使用与handleFileChange相同的处理逻辑
  handleFile(file)
}

// 检查文件类型是否符合accept要求
const isFileTypeAccepted = (file, accept) => {
  if (!accept) return true

  const acceptedTypes = accept.split(',').map(type => type.trim())
  const fileName = file.name || ''
  const fileType = file.type || ''

  return acceptedTypes.some(type => {
    // 处理.jpg这样的扩展名
    if (type.startsWith('.')) {
      return fileName.toLowerCase().endsWith(type.toLowerCase())
    }
    // 处理image/*这样的MIME类型
    else if (type.endsWith('/*')) {
      const baseType = type.slice(0, -2)
      return fileType.startsWith(baseType)
    }
    // 处理具体的MIME类型
    else {
      return fileType === type
    }
  })
}

// 提取文件处理逻辑为单独的方法，以便在点击和拖拽时复用
const handleFile = async (file) => {
  if (!file) return

  // 检查文件大小
  if (file.size > props.maxFileSize) {
    ElMessage.error(`文件大小不能超过 ${formatFileSize(props.maxFileSize)}`)
    return
  }

  selectedFile.value = file
  uploadSuccess.value = false
  uploadProgress.value = 0
  uploadedSize.value = 0
  fileReady.value = false // 重置文件准备状态

  // 计算文件MD5
  ElMessage.info('正在计算文件特征值，请稍候...')
  try {
    fileMd5.value = await calculateFileMd5(file)
    // 检查文件是否已存在（秒传）
    const checkResult = await checkFileExists(fileMd5.value)
    if (checkResult.exists) {
      fileUrl.value = checkResult.url
      handleUploadSuccess(fileUrl.value)
      ElMessage.success('文件已存在，秒传成功！')
    } else {
      // 准备分片上传
      prepareChunks()
    }
  } catch (error) {
    console.error('计算MD5失败', error)
    ElMessage.error('文件处理失败，请重试')
  }
}

// 修改handleFileChange方法，使用handleFile
const handleFileChange = (e) => {
  const file = e.target.files[0]
  handleFile(file)
}

// 其余方法保持不变
</script>

<style scoped>
.advanced-upload {
  width: 100%;
}

.upload-card {
  width: 100%;
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.upload-placeholder {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 200px;
  border: 2px dashed #dcdfe6;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s;
}

.upload-placeholder.drag-over {
  border-color: #409eff;
  background-color: rgba(64, 158, 255, 0.1);
}

.upload-placeholder .el-icon {
  font-size: 48px;
  color: #909399;
  margin-bottom: 10px;
  transition: all 0.3s;
}

.upload-placeholder.drag-over .el-icon {
  color: #409eff;
  transform: scale(1.2);
}

.upload-tip {
  font-size: 12px;
  color: #909399;
  margin-top: 10px;
}

.file-info {
  padding: 20px 0;
}

.file-name {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
}

.file-name .el-icon {
  margin-right: 8px;
  font-size: 20px;
  color: #409eff;
}

.file-size {
  margin-left: 8px;
  color: #909399;
  font-size: 14px;
}

.upload-progress {
  margin: 15px 0;
}

.progress-info {
  display: flex;
  justify-content: space-between;
  margin-top: 5px;
  font-size: 14px;
  color: #606266;
}

.upload-actions {
  margin-top: 15px;
  display: flex;
  gap: 10px;
}

.upload-success {
  display: flex;
  align-items: center;
  margin-top: 15px;
  color: #67c23a;
}

.upload-success .el-icon {
  font-size: 20px;
  margin-right: 8px;
}
</style>
